/*********************
* Author:Dzlua
* QQ:505544956
* time:2016/06/28
*/
#include "Window.h"
#include "../edui_sdl.h"
#include "SDL_syswm.h"

#ifdef EDUI_OS_WINDOWS
    #include <Windows.h>
    #pragma comment(lib, "User32.lib")
#endif

EDUI_NAMESPACE_BEGIN

#ifdef EDUI_TEST_NEW_DELETE
int Window::m_newdeleteNum = 0;
int Window::_GetNewDeleteNum()
{
    return m_newdeleteNum;
}
#endif

Window::Window(): m_wnd(0), m_wndID(0), m_font(0), m_parent(0), m_bEnabled(true), m_icon(0)
{
    m_name.Empty();
}

Window::~Window()
{}

Window* Window::GetInterface()
{
    Window* wnd = new Window;
#ifdef EDUI_TEST_NEW_DELETE
    ++m_newdeleteNum;
    Log("[Window] new window : %d", wnd);
#endif
#ifdef EDUI_USE_LUAPLUS
    LuaMgrPtr->Add("Window", wnd);
#endif
    return wnd;
}

void Window::Release()
{
#ifdef EDUI_TEST_NEW_DELETE
    --m_newdeleteNum;
    Log("[Window] delete window : %s, %d", this->GetName(), this);
#endif
#ifdef EDUI_USE_LUAPLUS
    LuaMgrPtr->Remove(this);
#endif
    delete this;
}

Window* Window::ToWindow(IWindow* wnd)
{
    return dynamic_cast<Window*>(wnd);
}

IRender* Window::GetRender()
{
    return &m_render;
}

IFont* Window::GetFont()
{
    return m_font;
}

bool Window::SetFont(IFont* font)
{
    if (!font) return false;
    m_font = font;
    return true;
}

bool Window::SetInputFocus()
{
    return SDL_SetWindowInputFocus(m_wnd) == 0;
}

void Window::StartTextInput()
{
    SDL_StartTextInput();
}

bool Window::IsTextInputActive()
{
    return SDL_IsTextInputActive() == SDL_TRUE;
}

void Window::StopTextInput()
{
    SDL_StopTextInput();
}

void Window::SetTextInputRect(Rect* rc)
{
    if (!rc) return;
    SDL_Rect sdlrc = { rc->x, rc->y, rc->w, rc->h };
    SDL_SetTextInputRect(&sdlrc);
}

bool Window::SetParent(IWindow* wnd)
{
    if (!wnd) return false;
    SDL_Window* parent = Window::ToWindow(wnd)->GetSDLWindow();

#ifdef EDUI_OS_WINDOWS
    auto getHWnd = [](SDL_Window *wnd) -> HWND {
        HWND hwnd = 0;
        SDL_SysWMinfo info = { 0 };
        if ( SDL_GetWindowWMInfo(wnd, &info) ) {
            hwnd = info.info.win.window;
        }
        return hwnd;
    };

    HWND hparent = getHWnd(parent);
    HWND hchild = getHWnd(m_wnd);
    if (!hchild || !hparent) {
        Log("[EDUI] Window::SetParent error SDL_GetWindowWMInfo : no hwnd.");
        return false;
    }

    if (!::SetParent(hchild, hparent)) {
        Log("[EDUI] Window::SetParent error : %s", ::GetLastError());
        return false;
    }

    return true;
#else
    if (0 == SDL_SetWindowModalFor(m_wnd, parent)){
        m_parent = wnd;
        return true;
    }
    Log("[EDUI] Window::SetParent error : %s", SDL_GetError());
    return false;
#endif    
}

IWindow* Window::GetParent() const
{
    return m_parent;
}

//0.0-1.0
bool Window::SetOpacity(float opacity)
{
    if (0 == SDL_SetWindowOpacity(m_wnd, opacity)) return true;
    Log("[EDUI] Window::SetOpacity error : %s", SDL_GetError());
    return false;
}

//-1:err;
float Window::GetOpacity()
{
    float fRet = -1.0f;
    if (0 == SDL_GetWindowOpacity(m_wnd, &fRet)) return fRet;
    return -1.0f;
}

const char* Window::GetName() const
{
    return m_name.GetString();
}

bool Window::SetName(const char* name)
{
    if (!name) return false;
    m_name = name;
    return true;
}

Uint32 Window::GetID() const
{
    return m_wndID;
}

Uint32 Window::GetFlags() const
{
    return SDL_GetWindowFlags(m_wnd);
}

SDL_HitTestResult _WindowHitTest(SDL_Window* win, const SDL_Point* area, void* data)
{
    Window* wnd = (Window*) data;
    if (!wnd || !wnd->IsEnabled()) return SDL_HITTEST_NORMAL;

    int w,h;
	SDL_GetWindowSize(win,&w,&h);

#ifdef EDUI_TEST_MOUSE_CONTROL
{
    Control* ctrl = wnd->FindControlByPt(area->x, area->y, FIND_NOVISIBLE);
    if (ctrl){
        Log("[HitTest] %s %d %s: %d %d", wnd->GetName(), ctrl, ctrl->GetName(), area->x, area->y);
    }
}
#endif

    if (SDL_GetWindowFlags(win) != SDL_WINDOW_MAXIMIZED){
		Rect1 rcSizeBox = wnd->GetSizeBox();
		if( area->y < rcSizeBox.t ) {
			if( area->x < rcSizeBox.l ) return SDL_HITTEST_RESIZE_TOPLEFT;
			if( area->x > (w - rcSizeBox.r) ) return SDL_HITTEST_RESIZE_TOPRIGHT;
			return SDL_HITTEST_RESIZE_TOP;
		}
		else if( area->y > (h - rcSizeBox.b)) {
			if( area->x <  rcSizeBox.l ) return SDL_HITTEST_RESIZE_BOTTOMLEFT;
			if( area->x >  (w - rcSizeBox.r)) return SDL_HITTEST_RESIZE_BOTTOMRIGHT;
			return SDL_HITTEST_RESIZE_BOTTOM;
		}
		if( area->x < rcSizeBox.l ) return SDL_HITTEST_RESIZE_LEFT;
		if( area->x > (w - rcSizeBox.r)) return SDL_HITTEST_RESIZE_RIGHT;
    }
    
    Rect rcCaption = wnd->GetCaptionRect();
	if (rcCaption.IsInRect(area->x, area->y)){
        Control* ctrl = wnd->FindControlByPt(area->x, area->y, FIND_NOVISIBLE);
        if (ctrl && !ctrl->IsDragWindow()){
            return SDL_HITTEST_NORMAL;
        }
	    return SDL_HITTEST_DRAGGABLE;
	}
    return SDL_HITTEST_NORMAL;
}

bool Window::Create(const char* name, const char* title, int x, int y, int w, int h, Uint32 flags, int index)
{
    if (!name) return false;
    if (m_wnd){
        Log("[EDUI] There's already a window.");
        return false;
    }
    m_wnd = SDL_CreateWindow(title, x, y, w, h, flags | SDL_WINDOW_HIDDEN);
    if (!m_wnd) return false;

    if (m_render.IsOK()) m_render.Destroy();    
    if(!m_render.Create(this)){
        this->Destroy();
        return false;
    }

    m_name = name;
    m_wndID = SDL_GetWindowID(m_wnd);
    if (!m_wndID){
        this->Destroy();
        return false;
    }

    if (SDL_SetWindowHitTest(m_wnd, _WindowHitTest, this) != 0){
        this->Destroy();
        return false;
    }

    WindowMgr* wndmgr = IEnginePtr->GetWindowMgr();
    if (!wndmgr->Add(name, this, index)){
        this->Destroy();
        return false;
    }

    FontMgr* fontMgr = IEnginePtr->GetFontMgr();
    m_font = fontMgr->GetByIndex(0);

    this->onCreate();
    
    return true;
}

bool Window::Create(const char* name, SDL_Window* sdlwnd, int index)
{
    if (!name || !sdlwnd) return false;
    if (m_wnd){
        Log("[EDUI] There's already a window.");
        return false;
    }
    
    m_wnd = sdlwnd;

    if (m_render.IsOK()) m_render.Destroy();
    if(!m_render.Create(this)){
        this->Destroy();
        return false;
    }

    m_name = name;
    m_wndID = SDL_GetWindowID(m_wnd);
    if (!m_wndID){
        this->Destroy();
        return false;
    }

    if (SDL_SetWindowHitTest(m_wnd, _WindowHitTest, this) != 0){
        this->Destroy();
        return false;
    }

    WindowMgr* wndmgr = IEnginePtr->GetWindowMgr();
    if (!wndmgr->Add(name, this, index)){
        this->Destroy();
        return false;
    }

    FontMgr* fontMgr = IEnginePtr->GetFontMgr();
    m_font = fontMgr->GetByIndex(0);

    this->onCreate();
    
    return true;
}

bool Window::IsOK()
{
    if (!m_wnd) return false;
    if (m_wndID <= 0) return false;
    if (!m_render.IsOK()) return false;
    return true;
}

void Window::Destroy()
{
    this->onDestroy();

    IEnginePtr->RemoveTimer(this);

    //controls
    if (m_root){
        m_root->Release();
    }

    //textures
    TextureMgr* textureMgr = this->GetTextureMgr();
    for (size_t i = 0; i < textureMgr->Count(); ++i){
        ITexture* texture = textureMgr->GetByIndex(i);
        if (texture){
            texture->Destroy();
            texture->Release();
        }
    }
    textureMgr->Clear();

    //animations
    AnimationMgr* animationMgr = this->GetAnimationMgr();
    for (size_t i = 0; i < animationMgr->Count(); ++i){
        Animation* animation = animationMgr->GetByIndex(i);
        if (animation){
            animation->Destroy();
            animation->Release();
        }
    }
    animationMgr->Clear();

    //render
    IRender* render = this->GetRender();
    render->Destroy();

    //window
    if (m_wnd){
        SDL_DestroyWindow(m_wnd);
        m_wnd = 0;
        m_wndID = 0;
        m_name.Empty();
    }
    
}

SDL_Window* Window::GetSDLWindow() const
{
    return m_wnd;
}

const char* Window::GetTitle() const
{
    return SDL_GetWindowTitle(m_wnd);
}

void Window::SetTitle(const char* title)
{
    SDL_SetWindowTitle(m_wnd, title);
}

void Window::SetIcon(ISurface* icon)
{
    if (!icon) return;
    m_icon = icon;
    SDL_SetWindowIcon(m_wnd, Surface::ToSurface(icon)->GetSDLSurface());
}

ISurface* Window::GetIcon()
{
    return m_icon;
}

bool Window::SetCaptionRect(const Rect* caption)
{
    if (!caption) return false;
    m_rcCaption = *caption;
    return true;
}

Rect Window::GetCaptionRect() const
{
    Rect rc = m_rcCaption;
    if (m_rcCaption.w == 0){
        rc.w = this->GetWidth();
    }
    if (m_rcCaption.h == 0){
        rc.h = this->GetHeight();
    }
    return rc;
}

bool Window::SetSizeBox(const Rect1* rcBox)
{
    if (!rcBox) return false;
    m_rcBox = *rcBox;
    return true;
}

Rect1 Window::GetSizeBox() const
{
    return m_rcBox;
}

int Window::GetWidth() const
{
    int w = 0;
    int h = 0;
    SDL_GetWindowSize(m_wnd, &w, &h);
    return w;
}

int Window::GetHeight() const
{
    int w = 0;
    int h = 0;
    SDL_GetWindowSize(m_wnd, &w, &h);
    return h;
}

Size Window::GetSize() const
{
    int w = 0;
    int h = 0;
    SDL_GetWindowSize(m_wnd, &w, &h);
    return Size(w, h);
}

Point Window::GetPos() const
{
    int x = 0;
    int y = 0;
    SDL_GetWindowPosition(m_wnd, &x, &y);
    return Point(x, y);
}

Rect Window::GetRect() const
{
    int x = 0;
    int y = 0;
    int w = 0;
    int h = 0;
    SDL_GetWindowPosition(m_wnd, &x, &y);
    SDL_GetWindowSize(m_wnd, &w, &h);
    return Rect(x, y, w, h);
}

Size Window::GetMaxSize() const
{
    int w = 0;
    int h = 0;
    SDL_GetWindowMaximumSize(m_wnd, &w, &h);
    return Size(w, h);
}

Size Window::GetMinSize() const
{
    int w = 0;
    int h = 0;
    SDL_GetWindowMinimumSize(m_wnd, &w, &h);
    return Size(w, h);
}

void Window::SetPos(int x, int y)
{
    SDL_SetWindowPosition(m_wnd, x, y);
}

void Window::SetSize(int w, int h)
{
    SDL_SetWindowSize(m_wnd, w, h);
}

void Window::SetRect(int x, int y, int w, int h)
{
    SDL_SetWindowPosition(m_wnd, x, y);
    SDL_SetWindowSize(m_wnd, w, h);
}

void Window::SetMaxSize(int w, int h)
{
    SDL_SetWindowMaximumSize(m_wnd, w, h);
}

void Window::SetMinSize(int w, int h)
{
    SDL_SetWindowMinimumSize(m_wnd, w, h);
}

void* Window::SetUserData(const char* name, void* data)
{
    return SDL_SetWindowData(m_wnd, name, data);
}

void* Window::GetUserData(const char* name) const
{
    return SDL_GetWindowData(m_wnd, name);
}

bool Window::SetFullScreen(WndFullScreen flags)
{
    int iRet = 1;
    switch (flags){
    case WND_FULLSCREEN_WINDOWED:
        iRet = SDL_SetWindowFullscreen(m_wnd, 0);
        break;
    case WND_FULLSCREEN_REAL:
        iRet = SDL_SetWindowFullscreen(m_wnd, SDL_WINDOW_FULLSCREEN);
        break;
    case WND_FULLSCREEN_DESKTOP:
        iRet = SDL_SetWindowFullscreen(m_wnd, SDL_WINDOW_FULLSCREEN_DESKTOP);
        break;
    default:
        break;
    }
    return !iRet;
}

void Window::MaxWindow() const
{
    SDL_MaximizeWindow(m_wnd);
}

void Window::MinWindow() const
{
    SDL_MinimizeWindow(m_wnd);
}

void Window::RestoreWindow() const
{
    SDL_RestoreWindow(m_wnd);
}

void Window::ShowWindow() const
{
    SDL_ShowWindow(m_wnd);
}

void Window::HideWindow() const
{
    SDL_HideWindow(m_wnd);
}

bool Window::IsEnabled() const
{
    return m_bEnabled;
}

void Window::SetEnabled(bool enabled)
{
    if (m_bEnabled == enabled) return;

    m_bEnabled = enabled;
}

void Window::onCreate()
{}

void Window::onDestroy()
{}

EDUI_NAMESPACE_END